package com.waverider.promotion.biz.service.impl;

import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.waverider.promotion.biz.service.ICache;

public class LocalCacheService implements ICache<Long, Object> {
	private Log logger = LogFactory.getLog(LocalCacheService.class);
	private ConcurrentHashMap<Long, Object>[] caches;
	private ConcurrentHashMap<Long, Long> expireCache;
	private ScheduledExecutorService scheduledExcutor;
	private int expireInterval = 10;
	private int moduleSize = 20;

	public LocalCacheService() {
		init();
	}

	public LocalCacheService(int expireInterval, int moduleSize) {
		this.expireInterval = expireInterval;
		this.moduleSize = moduleSize;
		init();
	}

	@SuppressWarnings("unchecked")
	private void init() {
		caches = new ConcurrentHashMap[moduleSize];
		for (int i = 0; i < moduleSize; i++) {
			caches[i] = new ConcurrentHashMap<Long, Object>();
		}
		expireCache = new ConcurrentHashMap<Long, Long>();
		scheduledExcutor = Executors.newScheduledThreadPool(1);
		scheduledExcutor.scheduleAtFixedRate(new CheckOutExpireDate(), 0,
				expireInterval * 60, TimeUnit.SECONDS);
		if (logger.isInfoEnabled())
			logger.info("cache start");
	}

	private ConcurrentHashMap<Long, Object> getCache(Long key) {
		int index = key.hashCode() % moduleSize;
		return caches[index];
	}

	private void checkValidate(Long key) {
		if (key != null && expireCache.get(key) != null
				&& expireCache.get(key).intValue() != -1
				&& new Date(expireCache.get(key)).before(new Date())) {
			getCache(key).remove(key);
			expireCache.remove(key);
		}
	}

	private void checkAll() {
		for (Long key : expireCache.keySet())
			checkValidate(key);
	}

	@Override
	public Object put(Long key, Object value) {
		expireCache.put(key, -1L);
		return getCache(key).put(key, value);
	}

	@Override
	public Object put(Long key, Object value, Date expire) {
		expireCache.put(key, expire.getTime());
		return getCache(key).put(key, value);
	}

	@Override
	public Object put(Long key, Object value, int TTL) {
		Calendar c = Calendar.getInstance();
		c.add(Calendar.SECOND, TTL);
		expireCache.put(key, c.getTime().getTime());
		return getCache(key).put(key, value);
	}

	@Override
	public Object get(Long key) {
		checkValidate(key);
		return getCache(key).get(key);
	}

	@Override
	public Object remove(Long key) {
		expireCache.remove(key);
		return getCache(key).get(key);
	}

	@Override
	public boolean clear() {
		for (Long key : expireCache.keySet()) {
			getCache(key).clear();
		}
		expireCache.clear();
		return true;
	}

	@Override
	public int size() {
		checkAll();
		return expireCache.size();
	}

	@Override
	public boolean containsKey(Long key) {
		checkValidate(key);
		return getCache(key).contains(key);
	}

	@Override
	public Set<Long> keySet() {
		checkAll();
		return expireCache.keySet();
	}

	@Override
	public Collection<Object> values() {
		Collection<Object> collection = new HashSet<Object>();
		checkAll();
		for (Long key : expireCache.keySet())
			collection.addAll(getCache(key).values());
		return collection;
	}

	@Override
	public void destory() {
		try {
			clear();
			if (scheduledExcutor != null)
				scheduledExcutor.shutdown();
			scheduledExcutor = null;
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
	}

	class CheckOutExpireDate implements Runnable {
		@Override
		public void run() {
			checkAll();
		}
	}
}
